在 Day2 提到過,Ruby
為單一繼承的語言。若我們要實現多重繼承的話,我們在 Day14 提到可以使用mixin
。今天要介紹的是 Ruby
程式語言內,Class
級別的繼承
# 後台退貨單controller
module Admin
class ReturnOrdersController < ApplicationController
# Day14 提到的Datatable module
include Datatable
end
end
class Admin::ApplicationController < ApplicationController
# admin 後台動作
end
Rails的專案中,我們一般會在後台管理介面的內容放在admin
,並給予NameSpace
為 Admin
。
從上方的關係我們看到,Admin::ReturnOrdersController
繼承了 Admin::ApplicationController
,而 Admin::ApplicationController
則繼承了ApplicationController
。我們用以下表格說明彼此的權責分工
Class in Controller | 說明 |
---|---|
Admin::ReturnOrdersController | 後台退貨單 Controller |
Admin::ApplicationController | 後台管理介面相關的 Controller 的動作 |
ApplicationController | 所有 Controller 的動作 |
Admin::ReturnOrdersController
的 ancestors
一共有下面這些。
Admin::ReturnOrdersController.ancestors
#=> [Admin::ReturnOrdersController, Datatable, Admin::ApplicationController, #<Module:0x00007fe5cf522800>, ApplicationController, ..., Object, ..., Kernel, BasicObject]
我們分別可以看到 Admin::ReturnOrdersController, Datatable, Admin::ApplicationController, ApplicationController
在繼承鍊的位置。
我們用 ancestors
的繼承鍊,可以看到繼承關係如下!
class A
end
class B < A
end
class C < B
end
C.ancestors
#=> [C, B, A, Object, Kernel, BasicObject]
我們習慣會稱 B為A的子類別SubClass
、C為B的子類別。如果看到外國文章提到SubClass
,記得就是指這種關係。
在使用繼承的方法時,可以使用#inherited
hook,我們可以在#inherited
新增實體方法跟類別方法。
class A
def self.inherited(subclass)
puts "Singleton class just got subclassed by #{subclass}"
#=> add instance method
subclass.class_eval do
end
#=> add class method
subclass.instance_eval do
end
end
end
class B < A
end
#=> Singleton class just got subclassed by B
當我們使用了super
的關鍵字,就可以取得繼承鍊上一層的內容。換句話說,若我們在繼承鍊寫super
,則可以避免覆寫方法。
下列例子中,StrongWarrior
繼承了戰士的屬性。
module Sword
mattr_accessor :material, default: :silver
def sword_name
[material.capitalize, 'Sword'].join(' ')
end
end
class Warrior
include Sword
attr_accessor :height, :weight, :tag
def initialize(height = nil, weight = nil)
@weight = weight
@height = height
end
def warrior_info
{
height: height,
weight: weight,
tag: tag
}
end
end
super
不帶參數,代表概括繼承。首先,我們使用沒有參數的super
。
class StrongWarrior < Warrior
def initialize(height, weight)
super
@tag = 'Strong'
end
end
warrior = StrongWarrior.new(172, 72)
warrior.warrior_info #=> {:height=>172, :weight=>72, :tag=>"Strong"}
super
帶參數的用法也可以
class WeekWarrior < Warrior
def initialize(height, weight)
super(height)
@tag = 'Week'
end
end
warrior = WeekWarrior.new(172, 72)
warrior.warrior_info #=> {:height=>172, :weight=>nil, :tag=>"Week"}
帶入空括號,代表不帶參數進去。
class NilWarrior < Warrior
def initialize(height, weight)
super()
@tag = 'Week'
end
end
warrior = NilWarrior.new(172, 72)
warrior.warrior_info #=> {:height=>nil, :weight=>nil, :tag=>"Week"}
⭐️ 由此可以知道,super
, super()
是完全不一樣的概念
super
可以在SubClass
的各種方法內使用。
class SuperWarrior < Warrior def initialize(height, weight) super @tag = 'Week' end def warrior_info { **super, a: 1, b: 2 } endendwarrior = SuperWarrior.new(172, 72)warrior.warrior_info #=> {:height=>172, :weight=>72, :tag=>"Week", :a=>1, :b=>2}
SuperWarrior#warrior_info
的概念是將原本的Warrior#warrior_info
額外填寫其他資料。
此外,Super
可以搭配yield
來使用喔!
class Warrior
attr_accessor :height, :weight, :tag
def initialize(height = nil, weight = nil)
@weight = weight
@height = height
end
def foo
block_given? ? yield : 'foo'
end
end
class YieldWarrior < Warrior
def initialize(height, weight)
super
@tag = 'Yield'
end
def foo
[super {"I am a good boy"}, "am I?"].join(', ')
end
end
warrior = YieldWarrior.new(172, 72)
warrior.foo #=> "I am a good boy, am I?"
繼承的基本概念是這樣,接著我們講Bonus的主題 ➡️ 如何取得該類別的各種方法
Day10-15 中,已經介紹了幾個方法
ancestors
instance_methods
singleton_methods
我們可以參考以下的方式篩選查看方法
User.instance_methods
#=> [:name_in_email, :email_upcase, ..., ..., ...]
#======= false ➡️ 不包含 ancestors 的方法 =======#
User.methods(false)
User.instance_methods(false)
#======= 用減集合來排除多餘的方法 =======#
User.methods - ActiveRecord::Base.methods
#======= grep =======#
string = "This is my IT challenge for 15th day."
string.methods.grep(/.!/).sort
#======= 取得 ancestors =======#
A.singleton_methods - A.singleton_methods(false)
這兩天分別講到module
, class
級別的繼承,大致上已經介紹了一個段落。
明天會開始介紹常用的設計流程